﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.IO.Compression;
using System.Drawing;

namespace FeiXunHelper.Services
{
    /// <summary>
    /// 抽象服务类
    /// </summary>
    public abstract class AbstractService
    {
        private string _userAgent = null;
        private WebHeaderCollection _requestHeaders = null;
        private Encoding _requestEncoding = null;

        public AbstractService()
        {
            _userAgent = "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36";
            _requestHeaders = new WebHeaderCollection();
            _requestHeaders.Add("Accept-Encoding", "gzip, deflate, sdch, br");
            _requestHeaders.Add("Accept-Language", "zh-CN,zh;q=0.8");
            _requestEncoding = Encoding.UTF8;
        }

        protected void SetRequestEncoding(Encoding encoding)
        {
            _requestEncoding = encoding;
        }

        protected void SetUserAgent(string userAgent)
        {
            _userAgent = userAgent;
        }

        protected void AddRequestHeader(string name, string value)
        {
            if (!_requestHeaders.AllKeys.Contains(name))
            {
                _requestHeaders.Add(name, value);
            }
        }

        protected HttpResult Get(string url, string referer, List<KeyValuePair<string, object>> paras, string cookie)
        {
            return Request(HttpMethod.Get, url, referer, paras, cookie);
        }

        protected HttpResult Post(string url, string referer, List<KeyValuePair<string, object>> paras, string cookie)
        {
            return Request(HttpMethod.Post, url, referer, paras, cookie);
        }

        protected HttpResult Request(HttpMethod method, string url, string referer, List<KeyValuePair<string, object>> paras, string cookie)
        {
            HttpResult result = new HttpResult();

            string realUrl = url;
            if (paras != null && method == HttpMethod.Get)
            {
                StringBuilder builder = new StringBuilder();
                foreach (var item in paras)
                {
                    builder.AppendFormat("{0}={1}&", item.Key, item.Value);
                }
                builder.Remove(builder.Length - 1, 1);
                realUrl = url + "?" + builder.ToString();
            }
            HttpWebRequest req = WebRequest.Create(realUrl) as HttpWebRequest;
            if (referer != null)
            {
                req.Referer = referer;
            }
            req.UserAgent = _userAgent;
            foreach (string name in _requestHeaders.AllKeys)
            {
                req.Headers.Add(name, _requestHeaders[name]);
            }
            if (cookie != null)
            {
                req.Headers.Add("Cookie", cookie);
            }
            req.Method = method.ToString().ToUpper();
            Stream stream = null;
            if (method == HttpMethod.Get)
            {
                //req.ContentType = "";
            }
            else if (method == HttpMethod.Post)
            {
                req.ContentType = "application/x-www-form-urlencoded;charset=UTF-8;";
                req.ServicePoint.Expect100Continue = false;
                if (paras != null)
                {
                    StringBuilder builder = new StringBuilder();
                    foreach (var item in paras)
                    {
                        builder.AppendFormat("{0}={1}&", item.Key, item.Value);
                    }
                    builder.Remove(builder.Length - 1, 1);
                    byte[] bytes = _requestEncoding.GetBytes(builder.ToString());
                    req.ContentLength = bytes.Length;
                    stream = req.GetRequestStream();
                    stream.Write(bytes, 0, bytes.Length);
                    stream.Close();
                }
            }
            HttpWebResponse resp = req.GetResponse() as HttpWebResponse;
            result.StatusCode = resp.StatusCode;
            result.StatusDescription = resp.StatusDescription;
            result.Header = resp.Headers;
            if (resp.Cookies != null)
                result.CookieCollection = resp.Cookies;
            if (resp.Headers["set-cookie"] != null)
                result.Cookie = resp.Headers["set-cookie"];
            result.ResultBytes = GetBytes(resp);

            return result;
        }

        /// <summary>
        /// 提取网页Byte
        /// </summary>
        /// <returns></returns>
        private byte[] GetBytes(HttpWebResponse resp)
        {
            byte[] ResponseByte = null;
            MemoryStream stream = new MemoryStream();

            //GZIIP处理
            if (resp.ContentEncoding != null && resp.ContentEncoding.Equals("gzip", StringComparison.InvariantCultureIgnoreCase))
            {
                //开始读取流并设置编码方式
                stream = GetMemoryStream(new GZipStream(resp.GetResponseStream(), CompressionMode.Decompress));
            }
            else
            {
                //开始读取流并设置编码方式
                stream = GetMemoryStream(resp.GetResponseStream());
            }
            //获取Byte
            ResponseByte = stream.ToArray();
            stream.Close();
            return ResponseByte;
        }

        /// <summary>
        /// 4.0以下.net版本取数据使用
        /// </summary>
        /// <param name="streamResponse">流</param>
        private MemoryStream GetMemoryStream(Stream streamResponse)
        {
            MemoryStream _stream = new MemoryStream();
            int Length = 256;
            Byte[] buffer = new Byte[Length];
            int bytesRead = streamResponse.Read(buffer, 0, Length);
            while (bytesRead > 0)
            {
                _stream.Write(buffer, 0, bytesRead);
                bytesRead = streamResponse.Read(buffer, 0, Length);
            }
            return _stream;
        }
    }

    /// <summary>
    /// Http请求方法
    /// </summary>
    public enum HttpMethod
    {
        Get,
        Post
    }

    /// <summary>
    /// Http返回参数类
    /// </summary>
    public class HttpResult
    {
        private string _cookie;
        private CookieCollection _cookieCollection;
        private byte[] _resultBytes = null;
        private string _html = null;
        private WebHeaderCollection _header;
        private string _statusDescription;
        private HttpStatusCode _statusCode;

        /// <summary>
        /// Http请求返回的Cookie
        /// </summary>
        public string Cookie
        {
            get { return _cookie; }
            set { _cookie = value; }
        }
        /// <summary>
        /// Cookie对象集合
        /// </summary>
        public CookieCollection CookieCollection
        {
            get { return _cookieCollection; }
            set { _cookieCollection = value; }
        }
        /// <summary>
        /// header对象
        /// </summary>
        public WebHeaderCollection Header
        {
            get { return _header; }
            set { _header = value; }
        }
        /// <summary>
        /// 返回状态说明
        /// </summary>
        public string StatusDescription
        {
            get { return _statusDescription; }
            set { _statusDescription = value; }
        }
        /// <summary>
        /// 返回状态码,默认为OK
        /// </summary>
        public HttpStatusCode StatusCode
        {
            get { return _statusCode; }
            set { _statusCode = value; }
        }
        /// <summary>
        /// 返回的Byte数组 只有ResultType.Byte时才返回数据，其它情况为空
        /// </summary>
        public byte[] ResultBytes
        {
            get { return _resultBytes; }
            set { _resultBytes = value; }
        }
        /// <summary>
        /// 返回的String类型数据，返回UTF8编码字符串
        /// </summary>
        public string GetHtml()
        {
            return GetHtml(Encoding.UTF8);
        }
        /// <summary>
        /// 返回的String类型数据，返回encoding编码字符串
        /// </summary>
        public string GetHtml(Encoding encoding)
        {
            _html = encoding.GetString(_resultBytes);
            return _html;
        }
        /// <summary>
        /// 返回的图像
        /// </summary>
        public Image GetImage()
        {
            return Image.FromStream(new MemoryStream(_resultBytes));
        }
    }

    /// <summary>
    /// HttpCookie
    /// </summary>
    public class HttpCookie
    {
        private Dictionary<string, string> _dic = new Dictionary<string, string>();
        private object _locker = new object();

        public void SetCookie(string key, string value)
        {
            lock (_locker)
            {
                if (!_dic.ContainsKey(key))
                    _dic.Add(key, string.Empty);
                _dic[key] = value;
            }
        }

        public void SetCookie(string cookie)
        {
            if (cookie == null || string.IsNullOrWhiteSpace(cookie))
                return;

            string[] parts = cookie.Split(',');
            // 去掉path关键字
            for (int i = 0; i < parts.Length; i++)
            {
                int index1 = parts[i].IndexOf("Path=", StringComparison.InvariantCultureIgnoreCase);
                if (index1 != -1)
                {
                    parts[i] = parts[i].Substring(0, index1);
                }
                index1 = parts[i].IndexOf("HttpOnly", StringComparison.InvariantCultureIgnoreCase);
                if (index1 != -1)
                {
                    parts[i] = parts[i].Substring(0, index1);
                }
            }
            cookie = string.Join("", parts);
            // 提取key-value
            parts = cookie.Split(';');
            for (int i = 0; i < parts.Length; i++)
            {
                string part = parts[i];
                // 过滤空白行
                if (part.Trim().Length == 0)
                    continue;
                string[] keyvalue = part.Split('=');
                if (keyvalue.Length != 2)
                    continue;
                string key = keyvalue[0].Trim();
                string value = keyvalue[1].Trim();
                SetCookie(key, value);
            }
        }

        public string GetCookie(string key)
        {
            lock (_locker)
            {
                if (!_dic.ContainsKey(key))
                    return null;
                return _dic[key];
            }
        }

        public void Clear()
        {
            _dic.Clear();
        }

        public bool IsEmpty()
        {
            return _dic.Count == 0;
        }

        public override string ToString()
        {
            if (_dic.Count == 0)
                return null;

            StringBuilder builder = new StringBuilder();
            foreach (KeyValuePair<string, string> pair in _dic)
            {
                builder.Append(pair.Key + "=" + pair.Value + ";");
            }
            return builder.ToString();
        }
    }
}
